﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using System.Runtime.Serialization;
using Microsoft.Ccr.Core;
using XcoAppSpaces.Contracts.Communication;
using XcoAppSpaces.Core;
using XcoAppSpaces.Contracts.Exceptions;
using System.Threading;
using XcoAppSpaces.Serialization.Json;

namespace test.XcoAppSpaces.Core.Special_Usage_Scenarios
{
	[Serializable]
	class NonDeserializable : ISerializable
	{
		public NonDeserializable() { }
		public NonDeserializable(SerializationInfo info, StreamingContext context)
		{ throw new Exception("Msg not deserializable!!"); }
		public void GetObjectData(SerializationInfo info, StreamingContext context) { }
	}

	class NonSerializable { }

	[Serializable]
	public class NonSerializable_Json { public NonSerializable_Json(string x){} }

	[Serializable]
	class NonSerializableContent
	{ public NonSerializable content = new NonSerializable(); }

	class PSerializationErrorWorker : PortSet<NonDeserializable, string> { }

	[Serializable]
	class RequestWithNonSerializableResponse
	{ public Port<NonSerializable> ResponsePort { get; set; } }

	[Serializable]
	class RequestWithNonSerializableResponseContent
	{ public Port<NonSerializableContent> ResponsePort { get; set; } }


	class SerializationErrorWorker : PSerializationErrorWorker
	{
		[XcoConcurrent]
		public void Process(NonDeserializable msg) { }
		[XcoConcurrent]
		public void Process(string msg) { }
	}

	//[Serializable]
	//class NonDeserializablePort : Port<string>, ISerializable
	//{
	//    public NonDeserializablePort()
	//    {
	//    }

	//    public NonDeserializablePort(SerializationInfo info, StreamingContext context)
	//    {
	//        throw new Exception("Port not deserializable!!");
	//    }
	//    public void GetObjectData(SerializationInfo info, StreamingContext context)
	//    {
	//    }
	//}

	[TestFixture]
	public class testSerializationErrorHandling
	{
		[Test]
		public void DeserializationError_WithCausality()
		{
			using (XcoAppSpace space1 = new XcoAppSpace("tcp.port=8001"), space2 = new XcoAppSpace("tcp.port=8002"))
			{
				space1.RunWorker<PSerializationErrorWorker, SerializationErrorWorker>();
				PSerializationErrorWorker worker = space2.ConnectWorker<PSerializationErrorWorker>(Environment.MachineName + ":8001");
				//add causality
				Port<Exception> exceptionPort = new Port<Exception>();
				Dispatcher.AddCausality(new Causality("c", exceptionPort));
				//post message
				worker.Post(new NonDeserializable());
				Dispatcher.RemoveCausality("c");
				//exception should have been received because of error while deserializing message
				Exception ex = exceptionPort.TestSync(2000);
				Assert.IsInstanceOf(typeof(XcoSerializationException), ex);
				
			}
		}

		[Test]
		public void DeserializationError_WithoutCausality()
		{
			using (XcoAppSpace space1 = new XcoAppSpace("tcp.port=8001"), space2 = new XcoAppSpace("tcp.port=8002"))
			{
				space1.RunWorker<PSerializationErrorWorker, SerializationErrorWorker>();
				PSerializationErrorWorker worker = space2.ConnectWorker<PSerializationErrorWorker>(Environment.MachineName + ":8001");
				//post message
				worker.Post(new NonDeserializable());
				//exception should have been received on common error port because of error while deserializing message
				XcoCommunicationError error = space2.Errors.TestSync(2000);
				Assert.IsInstanceOf(typeof(XcoSerializationException), error.Error);
				Console.WriteLine("Returned error info: " + error.Info);
			}
		}

		[Test]
		public void SerializationError_WithCausality()
		{
			using (XcoAppSpace space1 = new XcoAppSpace("tcp.port=8001"), space2 = new XcoAppSpace("tcp.port=8002"))
			{
				var w = new Port<NonSerializableContent>();
				space1.RunWorker(w);
				var worker = space2.Connect<NonSerializableContent>(Environment.MachineName + ":8001");
				//add causality
				Port<Exception> exceptionPort = new Port<Exception>();
				Dispatcher.AddCausality(new Causality("c", exceptionPort));
				//post message
				worker.Post(new NonSerializableContent());
				Dispatcher.RemoveCausality("c");
				//exception should have been received because of error while serializing message
				Exception ex = exceptionPort.TestSync(2000);
				Assert.IsInstanceOf(typeof(XcoSerializationException), ex);

			}
		}

		[Test]
		public void SerializationError_WithoutCausality()
		{
			using (XcoAppSpace space1 = new XcoAppSpace("tcp.port=8001"), space2 = new XcoAppSpace("tcp.port=8002"))
			{
				var w = new Port<NonSerializableContent>();
				space1.RunWorker(w);
				var worker = space2.Connect<NonSerializableContent>(Environment.MachineName + ":8001");
				//post message
				worker.Post(new NonSerializableContent());
				//exception should have been received on common error port because of error while serializing message
				XcoCommunicationError error = space2.Errors.TestSync(2000);
				Assert.IsInstanceOf(typeof(XcoSerializationException), error.Error);
				Console.WriteLine("Returned error info: " + error.Info);
			}
		}

		[Test]
		public void SerializationError_DuringResponse_WithCausality()
		{
			using (XcoAppSpace space1 = new XcoAppSpace("tcp.port=8001"), space2 = new XcoAppSpace("tcp.port=8002"))
			{
				space1.Run<RequestWithNonSerializableResponseContent>(msg => msg.ResponsePort.Post(new NonSerializableContent())); //a non serializable message is posted as response
				var worker = space2.Connect<RequestWithNonSerializableResponseContent>(Environment.MachineName + ":8001");
				//post message
				var responsePort = new Port<NonSerializableContent>();
				var exceptionPort = new Port<Exception>();
				worker.PostWithCausality(new RequestWithNonSerializableResponseContent { ResponsePort = responsePort }, exceptionPort);
				
				//exception should have been received because of error while serializing the response message
				Exception ex = exceptionPort.TestSync(2000);
				Assert.IsInstanceOf(typeof(XcoSerializationException), ex);
			}
		}

		[Test]
		public void SerializationError_DuringResponse_WithoutCausality()
		{
			using (XcoAppSpace space1 = new XcoAppSpace("tcp.port=8001"), space2 = new XcoAppSpace("tcp.port=8002"))
			{
				//set up a worker that posts a non serializable response
				space1.Run<RequestWithNonSerializableResponseContent>(msg => msg.ResponsePort.Post(new NonSerializableContent()));
				var worker = space2.Connect<RequestWithNonSerializableResponseContent>(Environment.MachineName + ":8001");
				//post message
				var responsePort = new Port<NonSerializableContent>();
				worker.Post(new RequestWithNonSerializableResponseContent { ResponsePort = responsePort });
				
				//exception should be received on common error port because of error while serializing the response message
				XcoCommunicationError error = space1.Errors.TestSync(2000);
				Assert.IsInstanceOf(typeof(XcoSerializationException), error.Error);
				Console.WriteLine("Returned error info: " + error.Info);
			}
		}

		[Test]
		[ExpectedException(typeof(XcoSerializationException))]
		public void CannotConnectToWorker_NonSerializableWorkerMessageType()
		{
			using (XcoAppSpace space1 = new XcoAppSpace("tcp.port=8001"), space2 = new XcoAppSpace("tcp.port=8002"))
			{
				var worker = new Port<NonSerializable>();
				space1.RunWorker(worker);
				space2.Connect<NonSerializable>(Environment.MachineName + ":8001");
			}
		}

		[Test]
		[ExpectedException(typeof(XcoSerializationException))]
		public void CannotConnectToWorker_NonSerializableResponseMessageType()
		{
			using (XcoAppSpace space1 = new XcoAppSpace("tcp.port=8001"), space2 = new XcoAppSpace("tcp.port=8002"))
			{
				var worker = new Port<RequestWithNonSerializableResponse>();
				space1.RunWorker(worker);
				space2.Connect<RequestWithNonSerializableResponse>(Environment.MachineName + ":8001");
			}
		}

		[Test]
		[ExpectedException(typeof(XcoSerializationException))]
		public void CannotConnectToWorker_NonSerializableMissingEmptyConstructor_Json()
		{
			using (XcoAppSpace space1 = XcoAppSpace.ConfigureWithConfigString("tcp.port=8001").UsingService<XcoJsonSerializer>() ,
				space2 = XcoAppSpace.ConfigureWithConfigString("tcp.port=8002").UsingService<XcoJsonSerializer>())
			{
				var worker = new Port<NonSerializable_Json>();
				space1.RunWorker(worker);
				space2.Connect<NonSerializable_Json>(Environment.MachineName + ":8001");
			}
		}

		//[Test] //NOTE: Is there actually a scenario where a causality can be non-deserializable??
		//public void TestDeserializationErrorWithNonDeserializableCausality()
		//{
		//    using (XcoAppSpace space1 = new XcoAppSpace("tcp.port=8001"), space2 = new XcoAppSpace("tcp.port=8002"))
		//    {
		//        space1.RunWorker<PSerializationErrorWorker, SerializationErrorWorker>();
		//        PSerializationErrorWorker worker = space2.ConnectWorker<PSerializationErrorWorker>(Environment.MachineName + ":8001");
		//        //add causality with coordination port that is not deserializable
		//        Port<Exception> exceptionPort = new Port<Exception>();
		//        NonDeserializablePort coordinationPort = new NonDeserializablePort();
		//        Dispatcher.AddCausality(new Causality("c", exceptionPort, coordinationPort));
		//        //post message
		//        worker.Post("test");
		//        //exception should have been received on common error port because of error while deserializing causality
		//        XcoCommunicationError error = space2.Errors.TestSync(2000);
		//        Assert.IsInstanceOfType(typeof(XcoSerializationException), error.Error);
		//        Assert.AreEqual(0, exceptionPort.ItemCount);
		//        Console.WriteLine("Returned error info: " + error.Info);
		//    }
		//}
	}
}
